//
//  TTRecordAdditions.m
//  DBManagerDemo
//
//  Created by CRY on 16/7/1.
//  Copyright © 2016年 CRY. All rights reserved.
//

#import "TRAdditions.h"
#import "TRManager.h"
#import "TRTools.h"
#import "FMDB.h"
#import <objc/runtime.h>

@interface TRAdditions()

@property (nonatomic, copy  ) NSString     *tableName;
@property (nonatomic, copy  ) NSString     *sql;
@property (nonatomic, strong) TRManager    *manager;
@property (nonatomic, strong) NSString     *primaryKey;

@end

@implementation TRAdditions
{
    BOOL _isTableExist;
}

+ (void)saveToPersistentStoreAndWaitWithObject:(NSObject *)object
                                    primaryKey:(NSString *)primaryKey
                                      finished:(FinishedBlock)finished{
    
    TRAdditions *addition = [TRAdditions additions];
    addition.obj          = object;
    addition.primaryKey   = primaryKey;
    [addition saveToPersistentStoreAndWaitWithBlock:finished];
}

+ (NSArray *)findAllWithObject:(id)obj
                     parameter:(NSArray *)param
            orderedByAttribute:(NSString *)attribute
                     ascending:(BOOL)ascending
                         limit:(NSInteger)limit
                        offset:(NSInteger)offset{
    
    TRAdditions *addition = [TRAdditions additions];
    addition.obj          = obj;
    return [addition findByParameter:param
                  orderedByAttribute:attribute
                           ascending:ascending
                               limit:limit
                              offset:offset];
}

+ (void)deleteWithObject:(id)obj finished:(FinishedBlock)finished{
    
    TRAdditions *addition = [TRAdditions additions];
    addition.obj          = obj;
    return [addition deleteWithFinished:finished];
}

+ (instancetype)additions{
    
    static dispatch_once_t onceToken;
    static TRAdditions * obj = nil;
    dispatch_once(&onceToken, ^{
        obj = [[self alloc] init];
    });
    return obj;
}

- (instancetype)init{
    
    if (self = [super init]) {
        _manager = [TRManager manager];
        _isTableExist = NO;
    }
    return self;
}
#pragma mark - find
- (NSArray *)findByParameter:(NSArray *)parameter
          orderedByAttribute:(NSString *)attribute
                   ascending:(BOOL)ascending
                       limit:(NSInteger)limit
                      offset:(NSInteger)offset{
    
    if (![self getTableNameWithObject:_obj])
        return nil;
    // 检查数据库中是否存在该表
    if (![[_manager selectDataWithSql:SQL_TABLE_EXIST(_tableName)] next]) return [NSArray array];
    [self getSelectSqlWithObject:_obj
                       parameter:parameter
              orderedByAttribute:(NSString *)attribute
                       ascending:(BOOL)ascending
                           limit:limit
                          offset:offset];
    FMResultSet *rs = [_manager selectDataWithSql:_sql];
    NSMutableArray *modelArray = [NSMutableArray array];
    while ([rs next]) {
        
        Class class = NSClassFromString(_tableName);
        NSObject *model = [[class alloc] init];
        NSArray *propArr = [TRTools propertiesWithObject:_obj];
        for (NSString *prop in propArr) {
            id value = [rs objectForColumnName:prop];
            if (value == [NSNull null] || value == nil) {
                continue;
            }
            [model setValue:value forKey:prop];
        }
        [modelArray addObject:model];
    }
    return modelArray;
}
#pragma mark - delete
- (void)deleteWithFinished:(FinishedBlock)finished{
    
    NSDictionary *dic = [TRTools properties_apsWithObject:_obj];
    NSMutableArray *params = [NSMutableArray array];
    for (NSString *param in dic) {
        [params addObject:[NSString stringWithFormat:@"%@=%@",param, dic[param]]];
    }
    if ([[self findByParameter:params orderedByAttribute:nil ascending:NO limit:0 offset:0] count] > 0) {
        [self getDeleteSqlWithObject:_obj parameter:dic];
        [_manager deleteDataWithSql:_sql isSuccess:^(BOOL isSuccess) {
            if (isSuccess) {
                if (finished != nil) {
                   finished();
                }
            }
            debugLog(@"数据删除%@！",isSuccess?@"成功":@"失败");
        }];
    }else{
        debugLog(@"数据删除失败,数据库中没有这条数据！");
    }
}

#pragma mark - save
- (void)saveToPersistentStoreAndWaitWithBlock:(FinishedBlock)finished{
    
    [self checkAndFixTable];
    // 存储过程
    NSString *sql_select = [NSString stringWithFormat:@"%@ where %@='%@'",_tableName,_primaryKey,[_obj valueForKey:_primaryKey]];
    if (![[_manager selectDataWithSql:sql_select] next]) {
        [self getTableNameWithObject:_obj];
        [self getInsertSqlWithObject:_obj];
        [_manager insertDataWithSql:_sql isSuccess:^(BOOL isSuccess) {
            if (isSuccess) {
                // block应做非nil判断
                if (finished != nil) {
                    finished();
                }
            }
            debugLog(@"数据插入%@！",isSuccess?@"成功":@"失败");
        }];
    }else{
        debugLog(@"ERROR---主键数据重复！");
    }
}
/**
 *  修正表结构，用于数据结构更新时存在的问题
 *
 *  @return 返回处理结果是否成功
 */
- (BOOL)checkAndFixTable{
    
    if (_obj == nil) return NO;
    if (![self getTableNameWithObject:_obj])
        return NO;
    if (![self getCreateSqlWithObject:_obj])
        return NO;
//    判断表是否存在
    if (_isTableExist) {
        if ([TRTools isNewVersion]) {
            FMResultSet *result = [_manager executeQuery:SQL_TABLE_LISTNAME(_tableName)];
            [self updateTableListsWithResultset:result];
        }
    }else{
        [_manager createTable:_tableName andSql:_sql];
        _isTableExist = YES;
    }
    return YES;
}
// TODO
// 这里如果类中添加了字段，则会在表中添加相应字段，如果减少了某些字段，则数据库中不做处理
// 这里暂时不支持类型修改，即在类中修改了某属性的存储类型，则数据库中不会做出相应的处理
// 暂时不支持的功能是为了最大限度的保存数据
- (void)updateTableListsWithResultset:(FMResultSet *)result{
    
    NSMutableDictionary *listDict = [NSMutableDictionary dictionary];
    while (result.next) {
        [listDict setObject:[result stringForColumn:@"type"]
                     forKey:[result stringForColumn:@"name"]];
    }
    NSDictionary *classDict = [TRTools properties_attrWithObject:_obj];
    for (NSString *attr in classDict) {
        
        if (listDict[attr] == nil) {
            
            NSString *alter_Sql = SQL_TABLE_ALTER(_tableName, attr, [TRTools getSqlTypeWithAttribute:classDict[attr]]);
            [_manager executeUpdate:alter_Sql];
        }
    }
}
#pragma mark - tool
// 获取表名
- (BOOL)getTableNameWithObject:(NSObject *)obj{
    
    if (obj == nil) return NO;
    _tableName = NSStringFromClass([obj class]);
    return YES;
}
#pragma mark - sql
// sql语句_创建表
- (BOOL)getCreateSqlWithObject:(NSObject *)obj{
    
    if (obj == nil) return NO;
    NSDictionary *propAttrArray = [TRTools properties_attrWithObject:obj];
    NSString *sql = [NSString string];
    for (NSString *propName in propAttrArray) {
        NSString *propType = [TRTools getSqlTypeWithAttribute:propAttrArray[propName]];
        if (propType == nil) continue;
        sql = [sql stringByAppendingFormat:@"%@ %@,",propName,propType];
    }
    _sql = [sql substringToIndex:sql.length-1];
    return YES;
}
// sql语句_插入数据
- (BOOL)getInsertSqlWithObject:(NSObject *)obj{
    
    if (obj == nil) return NO;
    if ([obj valueForKey:_primaryKey] == nil) {
        debugLog(@"ERROR---主键未赋值！");
        return NO;
    }
    NSDictionary *propAttrDict = [TRTools properties_apsWithObject:obj];
    NSString *sql_key   = [NSString string];
    NSString *sql_value = [NSString string];
    for (NSString *propName in propAttrDict) {
        if (propAttrDict[propName] == nil) continue;
        sql_key = [sql_key stringByAppendingFormat:@"%@,",propName];
        sql_value = [sql_value stringByAppendingFormat:@"'%@',",propAttrDict[propName]];
    }
    
    _sql = [_tableName stringByAppendingFormat:@"(%@) values(%@)",[sql_key substringToIndex:sql_key.length-1],[sql_value substringToIndex:sql_value.length-1]];
    return YES;
}

- (void)getSelectSqlWithObject:(NSObject *)obj
                     parameter:(NSArray *)param
            orderedByAttribute:(NSString *)attribute
                     ascending:(BOOL)ascending
                         limit:(NSInteger)limit
                        offset:(NSInteger)offset{
    
    if (obj == nil) return;
    if (param == nil || param.count == 0) {
        
        _sql = [_tableName copy];
        if (attribute != nil) {
            _sql = [_sql stringByAppendingFormat:@" order by %@ %@",attribute,ascending?@"ASC":@"DESC"];
        }
        return;
    }
    NSString *sql = [NSString string];
    for (NSString *condStr in param) {

        NSString *conditionStr = [[condStr componentsSeparatedByString:@" "] componentsJoinedByString:@""];
        if ([conditionStr componentsSeparatedByString:@"="].count == 2) {
            NSArray *conds = [conditionStr componentsSeparatedByString:@"="];
            sql = [sql stringByAppendingFormat:@"%@ = '%@' and ",conds[0],conds[1]];
        }
        if ([conditionStr componentsSeparatedByString:@">"].count == 2) {
            NSArray *conds = [conditionStr componentsSeparatedByString:@">"];
            sql = [sql stringByAppendingFormat:@"%@ > '%@' and ",conds[0],conds[1]];
        }
        if ([conditionStr componentsSeparatedByString:@"<"].count == 2) {
            NSArray *conds = [conditionStr componentsSeparatedByString:@"<"];
            sql = [sql stringByAppendingFormat:@"%@ < '%@' and ",conds[0],conds[1]];
        }
    }

    _sql = [[_tableName stringByAppendingString:@" where "]
             stringByAppendingString:[sql substringToIndex:sql.length - 5]];
    if (attribute != nil) {
        _sql = [_sql stringByAppendingFormat:@" order by %@ %@",attribute,ascending?@"ASC":@"DESC"];
    }
    if (limit != 0) {
        _sql = [_sql stringByAppendingFormat:@" limit %ld offset %ld",(long)limit,(long)offset];
    }
    debugLog(@"sql:%@",_sql);
}

- (void)getDeleteSqlWithObject:(NSObject *)obj
                     parameter:(NSDictionary *)param{
    
    NSString *sql = [NSString string];
    for (NSString *key in param) {
        if (param[key] == nil) continue;
        sql = [sql stringByAppendingFormat:@"%@='%@' and ",key,param[key]];
    }
    _sql = [[_tableName stringByAppendingString:@" where "]
            stringByAppendingString:[sql substringToIndex:sql.length - 5]];
}

@end
